﻿namespace IOPvsOOP.Visitor.NOIF
{
    public abstract class Employee
    {
        protected Employee(int days)
        {
            Days = days;
        }

        public string Name { get; set; }
        public string Last { get; set; }
        public string Number { get; set; }
        public int Days { get; set; }

        public abstract T Ask<T>(IEmployeeVisitor<T> visitor);
    }

    public class Director : Employee
    {
        public Director(int days) : base(days) { }

        public override T Ask<T>(IEmployeeVisitor<T> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public class Executive : Employee
    {
        public Executive(int days) : base(days) { }
        
        public override T Ask<T>(IEmployeeVisitor<T> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public class Manager : Employee
    {
        public Manager(int days) : base(days) { }
        
        public override T Ask<T>(IEmployeeVisitor<T> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public class Leader : Employee
    {
        public Leader(int days) : base(days) { }
        
        public override T Ask<T>(IEmployeeVisitor<T> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public class OfficeWorker : Employee
    {
        public OfficeWorker(int days) : base(days) { }

        public override T Ask<T>(IEmployeeVisitor<T> visitor)
        {
            return visitor.Visit(this);
        }
    }

    public interface IEmployeeVisitor<out T>
    {
        T Visit(Director employee);
        T Visit(Manager employee);
        T Visit(Executive employee);
        T Visit(Leader employee);
        T Visit(OfficeWorker employee);
    }

    public class SalaryCalculationVisitor : IEmployeeVisitor<double>
    {
        public double Visit(Director employee)
        {
            return employee.Days * 100;
        }

        public double Visit(Manager employee)
        {
            return employee.Days * 80;
        }

        public double Visit(Executive employee)
        {
            return employee.Days * 80;
        }

        public double Visit(Leader employee)
        {
            return employee.Days * 45;
        }

        public double Visit(OfficeWorker employee)
        {
            return employee.Days * 23;
        }
    }

    public class SalaryBenefitCalculationVisitor : IEmployeeVisitor<double>
    {
        private readonly IEmployeeVisitor<double> inner;

        public SalaryBenefitCalculationVisitor(IEmployeeVisitor<double> inner)
        {
            this.inner = inner;
        }

        public double Visit(Director employee)
        {
            return Benefit(inner.Visit(employee), 3);
        }

        public double Visit(Manager employee)
        {
            return Benefit(inner.Visit(employee), 5);
        }

        public double Visit(Executive employee)
        {
            return Benefit(inner.Visit(employee), 5);
        }

        public double Visit(Leader employee)
        {
            return Benefit(inner.Visit(employee), 10);
        }

        public double Visit(OfficeWorker employee)
        {
            return Benefit(inner.Visit(employee), 20);
        }

        private static double Benefit(double result, int ratio)
        {
            return result + result / 100 * ratio;
        }
    }
}